package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.mapper.AlbumAttributeValueMapper;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.service.AlbumAttributeValueService;
import com.atguigu.tingshu.album.service.AlbumInfoService;
import com.atguigu.tingshu.common.Cache.MyCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.MyException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumAttributeValue;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.query.album.AlbumInfoQuery;
import com.atguigu.tingshu.vo.album.AlbumAttributeValueVo;
import com.atguigu.tingshu.vo.album.AlbumInfoVo;
import com.atguigu.tingshu.vo.album.AlbumListVo;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class AlbumInfoServiceImpl extends ServiceImpl<AlbumInfoMapper, AlbumInfo> implements AlbumInfoService {

    @Autowired
    private AlbumInfoMapper albumInfoMapper;

    @Autowired
    private AlbumAttributeValueMapper albumAttributeValueMapper;

    @Autowired
    private AlbumStatMapper albumStatMapper;

    @Autowired
    private TrackInfoMapper trackInfoMapper;

    @Autowired
    private RabbitService rabbitService;

    @Override
    public Boolean saveAlbumInfo(AlbumInfoVo albumInfoVo) {
        Boolean flag = false;

        //album_info 专辑表
        AlbumInfo albumInfo = new AlbumInfo();
        BeanUtils.copyProperties(albumInfoVo, albumInfo);
        Long userId = AuthContextHolder.getUserId();
        albumInfo.setUserId(userId);
        System.out.println("1111" + userId);
        albumInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
        albumInfo.setTracksForFree(albumInfoVo.getPayType().equals(SystemConstant.ALBUM_PAY_TYPE_FREE) ? 5 : 0);
        int i1 = albumInfoMapper.insert(albumInfo);
        Long albumId = albumInfo.getId();
        flag = i1 != 0;

        //album_attribute_value 专辑属性值表
        //原来只添加了一条瞎写的
        //正常应该从传入的albumInfoVo里get嘛 里边是个list
//        AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
//        BeanUtils.copyProperties(albumInfoVo, albumAttributeValue);
//        Long albumId = new Long(444L);
//        albumAttributeValue.setAlbumId(albumId);
//        int i2 = albumAttributeValueMapper.insert(albumAttributeValue);

        List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        //先判断是否为空
        if (!CollectionUtils.isEmpty(albumAttributeValueVoList)) {
            for (AlbumAttributeValueVo albumAttributeValueVo : albumAttributeValueVoList) {
                AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
                BeanUtils.copyProperties(albumAttributeValueVo, albumAttributeValue);
                //设置专辑id
                albumAttributeValue.setAlbumId(albumId);
                int i2 = albumAttributeValueMapper.insert(albumAttributeValue);
                flag = i2 != 0;
            }
        }

        //album_stat 专辑统计表
        this.saveAlbumStat(albumId, SystemConstant.ALBUM_STAT_PLAY);
        this.saveAlbumStat(albumId, "0402");
        this.saveAlbumStat(albumId, "0403");
        this.saveAlbumStat(albumId, "0404");

        if ("1".equals(albumInfo.getIsOpen())) {
            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumInfo.getId());
        }
        return flag;
    }

    @Override
    public IPage<AlbumListVo> findUserAlbumPage(Long page, Long limit, AlbumInfoQuery albumInfoQuery) {
        return albumInfoMapper.selectUserAlbumPage(new Page<>(page, limit), albumInfoQuery);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean removeAlbumInfo(Long id) {

        //1 判断：如果专辑里面存在声音，不能删除
        //根据专辑id查询!!!!"声音表"!!!!track_info
        LambdaQueryWrapper<TrackInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TrackInfo::getAlbumId, id);
        //SELECT COUNT(*) FROM track_info WHERE album_id=1603
        Long count = trackInfoMapper.selectCount(wrapper);
        if (count > 0) {//专辑有声音
            throw new MyException(400, "该专辑下存在未删除声音！");
        }

        boolean b = albumInfoMapper.deleteById(id) != 0;
        b = albumAttributeValueMapper.delete(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, id)) != 0;
        b = albumStatMapper.delete(new LambdaQueryWrapper<AlbumStat>().eq(AlbumStat::getAlbumId, id)) != 0;

        rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, id);
        return b;
    }

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    //优化通过id获取专辑详情 完整版未使用注解
    /*@Override
    public AlbumInfo getAlbumInfo(Long id) {
        初始版
        AlbumInfo albumInfo = albumInfoMapper.selectById(id);
        //因为AlbumInfo实体类里有额外属性，不在对应表中
        //所以这里需要额外通过id查一下，不能用selectById不是他的id
        //所以又用到了LambdaQueryWrapper
        List<AlbumAttributeValue> albumAttributeValues = albumAttributeValueMapper.selectList(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, id));
        //AlbumInfo里的ValueVoList其实是ValueList
        albumInfo.setAlbumAttributeValueVoList(albumAttributeValues);
        return albumInfo;


        //使用redisson 优化通过id专辑详情
        //使用redisson 为专辑详情添加缓存 缓存没有访问mysql 需要考虑缓存击穿 缓存分布式锁
        //生成本次查询的key，在缓存中找
        String key = RedisConstant.ALBUM_INFO_PREFIX + id;
        AlbumInfo albumInfo = (AlbumInfo) redisTemplate.opsForValue().get(key);

        //如果没有访问mysql，并将专辑详情添加到缓存
        if (albumInfo == null) {
            try {
                //生成锁的key和锁
                String lockKey = RedisConstant.ALBUM_INFO_PREFIX + id + "lock";
                RLock rLock = redissonClient.getLock(lockKey);

                //有lock、trylock、空的lock  参数是过期时间和时间单位trylock多了个等待时间
                boolean tryLock = rLock.tryLock(10, 30, TimeUnit.SECONDS);
                if (tryLock) {
                    try {
                        AlbumInfo albumInfoData = getAlbumInfoData(id);
                        if (albumInfoData == null) {
                            albumInfoData = new AlbumInfo();
                        }
                        // 商品如果在数据库中不存在那么会缓存一个空对象进去，但是这个对象是没有用的，所以这个对象的过期时间应该不能太长，
                        // 如果太长会占用内存。 封装了个常量类，缓存60分钟
                        redisTemplate.opsForValue().set(key, albumInfoData, RedisConstant.ALBUM_TIMEOUT, TimeUnit.SECONDS);
                        return albumInfoData;

                        //最后无论如何需要解锁，就算报异常也解锁，所以使用try finally实现解锁
                    } finally {
                        rLock.unlock();
                    }

                } else {
                    //没抢到锁就一直抢 自旋
                    return getAlbumInfo(id);
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        } else {
            return albumInfo;
        }
    }
    */

    //修改- 根据专辑id查询专辑信息
    @MyCache(prefix="album:Info")
    @Override
    public AlbumInfo getAlbumInfo(Long id) {
        AlbumInfo albumInfo = albumInfoMapper.selectById(id);
        List<AlbumAttributeValue> albumAttributeValues = albumAttributeValueMapper.selectList(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, id));
        albumInfo.setAlbumAttributeValueVoList(albumAttributeValues);
        return albumInfo;
    }

    @Autowired
    private AlbumAttributeValueService albumAttributeValueService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateAlbumInfo(Long id, AlbumInfoVo albumInfoVo) {

        Boolean flag = false;
        //1
        //因为AlbumInfo实体类里有额外属性，所以分为两部分
//        AlbumInfo albumInfo = albumInfoMapper.selectById(id);
        AlbumInfo albumInfo = albumInfoMapper.selectById(id);

        BeanUtils.copyProperties(albumInfoVo, albumInfo);
        flag = albumInfoMapper.updateById(albumInfo) != 0;

        //2
        LambdaQueryWrapper<AlbumAttributeValue> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.eq(AlbumAttributeValue::getAlbumId, id);
        albumAttributeValueMapper.delete(wrapper1);

        List<AlbumAttributeValueVo> albumAttributeValueVoList = albumInfoVo.getAlbumAttributeValueVoList();
        if (!CollectionUtils.isEmpty(albumAttributeValueVoList)) {
            List<AlbumAttributeValue> collect = albumAttributeValueVoList.stream().map(vo -> {
                AlbumAttributeValue albumAttributeValue = new AlbumAttributeValue();
                BeanUtils.copyProperties(vo, albumAttributeValue);
                albumAttributeValue.setAlbumId(id);
                return albumAttributeValue;
            }).collect(Collectors.toList());
            albumAttributeValueService.saveBatch(collect);
        }

//        if ("1".equals(albumInfo.getIsOpen())) {
//            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_UPPER, albumInfo.getId());
//        } else {
//            rabbitService.sendMessage(MqConst.EXCHANGE_ALBUM, MqConst.ROUTING_ALBUM_LOWER, albumInfo.getId());
//        }
        return flag;
    }

    @Override
    public List<AlbumInfo> findUserAllAlbumList() {
        Page page = new Page(1, 100);
        Long userId = AuthContextHolder.getUserId();
        List<AlbumInfo> albumInfoList = albumInfoMapper.selectPage(page,
                new LambdaQueryWrapper<AlbumInfo>()
                        .select(AlbumInfo::getId, AlbumInfo::getAlbumTitle)
                        .eq(AlbumInfo::getUserId, userId)
                        .orderByDesc(AlbumInfo::getId)
        ).getRecords();
//        List<AlbumInfo> albumInfos = albumInfoMapper.selectList(new LambdaQueryWrapper<AlbumInfo>()
//                .select(AlbumInfo::getId, AlbumInfo::getAlbumTitle)
//                .eq(AlbumInfo::getUserId, userId)
//                .orderByDesc(AlbumInfo::getId));
        return albumInfoList;
    }

    @Override
    public Map<String, Object> getAlbumStat(Long albumId) {
        return albumStatMapper.selectAlbumStatMapByAlbumId(albumId);
    }

    @Override
    public AlbumStatVo getAlbumStatVoByAlbumId(Long albumId) {
        AlbumStatVo albumStatVo = albumStatMapper.getAlbumStatVoByAlbumId(albumId);
        return albumStatVo;
    }

    private int saveAlbumStat(Long albumId, String statType) {
        return albumStatMapper.insert(new AlbumStat(albumId, statType, 0));
    }

    //通过id获取AlbumInfo
    private AlbumInfo getAlbumInfoData(Long id) {
        AlbumInfo albumInfo = albumInfoMapper.selectById(id);
        //因为AlbumInfo实体类里有额外属性，不在对应表中
        //所以这里需要额外通过id查一下，不能用selectById不是他的id
        //所以又用到了LambdaQueryWrapper
        List<AlbumAttributeValue> albumAttributeValues = albumAttributeValueMapper.selectList(new LambdaQueryWrapper<AlbumAttributeValue>().eq(AlbumAttributeValue::getAlbumId, id));
        //AlbumInfo里的ValueVoList其实是ValueList
        albumInfo.setAlbumAttributeValueVoList(albumAttributeValues);
        return albumInfo;
    }
}
